/*
 * Copyright (C) 2009 IMOBISOFT Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package imobisoft.android.hialarm.base;

import java.util.HashMap;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.media.RingtoneManager;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.MediaStore;


/**
 * 
 * HiAlarm utility and const class
 *
 */
public class AlarmConst{
	public static final String TAG = "HiAlarm";
	
	public final static String ID = "alarm_id";
	
	/**
	 * index to Uri
	 */
	private  final static HashMap<Integer, Uri> mRingtoneList = new HashMap<Integer, Uri>();
	/**
	 * Uri to index
	 */
	private  final static HashMap<Uri, Integer> mRingtoneIndexList =  new HashMap<Uri, Integer>();
	 /**
     * getAlarm and getAlarms call this interface to report alarms in
     * the database
     */
    public static interface AlarmSettings {
        void reportAlarm(
                int idx, boolean enabled, int hour, int minutes,
                DaysOfWeek daysOfWeek, boolean vibrate, String message,
                String alert);
        
        void reportRingtones(Cursor cursor);
    }
    
    public static interface IAlarmRingtone{
    	void reportRingtoneUri(Uri uri);
    	void reportRingtoneIndex(int index);
    }
	
	/**
	 * 
	 * Track day set ,e.g Mon,Wed,Tue etc
	 *
	 */
	public static class DaysOfWeek {
        int mDays;

		/**
		 * Days of week coded as single int, convenient for DB storage:
		 * 
		 * 0x00: no day 0x01: Monday 0x02: Tuesday 0x04: Wednesday 0x08:
		 * Thursday 0x10: Friday 0x20: Saturday 0x40: Sunday
		 */
		public DaysOfWeek() {
			this(0);
		}

		DaysOfWeek(int days) {
			mDays = days;
		}

		public void set(int day, boolean set) {
			if (set) {
				mDays |= (1 << day);
			} else {
				mDays &= ~(1 << day);
			}
		}

		public void set(DaysOfWeek dow) {
			mDays = dow.mDays;
		}
		
		 /**
         * @param day Mon=0 ... Sun=6
         * @return true if given day is set
         */
        public boolean isSet(int day) {
            return ((mDays & (1 << day)) > 0);
        }
		
		 // Returns days of week encoded in an array of booleans.
        public boolean[] getBooleanArray() {
            boolean[] ret = new boolean[7];
            for (int i = 0; i < 7; i++) {
                ret[i] = isSet(i);
            }
            return ret;
        }

	}
	
	public static class AlarmColumns implements BaseColumns {
        /**
         * The content:// style URL for this table
         */
        public static final Uri CONTENT_URI =
            Uri.parse("content://imobisoft.android.hialarm/alarm");

        public static final String _ID = "_id";

        /**
         * The default sort order for this table
         */
        public static final String DEFAULT_SORT_ORDER = "_id ASC";

        /**
         * Hour in 24-hour localtime 0 - 23.
         * <P>Type: INTEGER</P>
         */
        public static final String HOUR = "hour";

        /**
         * Minutes in localtime 0 - 59
         * <P>Type: INTEGER</P>
         */
        public static final String MINUTES = "minutes";

        /**
         * Days of week coded as integer
         * <P>Type: INTEGER</P>
         */
        public static final String DAYS_OF_WEEK = "daysofweek";

        /**
         * Alarm time in UTC milliseconds from the epoch.
         * <P>Type: INTEGER</P>
         */
        public static final String ALARM_TIME = "alarmtime";

        /**
         * True if alarm is active
         * <P>Type: BOOLEAN</P>
         */
        public static final String ENABLED = "enabled";

        /**
         * True if alarm should vibrate
         * <P>Type: BOOLEAN</P>
         */
        public static final String VIBRATE = "vibrate";

        /**
         * Message to show when alarm triggers
         * Note: not currently used
         * <P>Type: STRING</P>
         */
        public static final String MESSAGE = "message";

        /**
         * Audio alert to play when alarm triggers
         * <P>Type: STRING</P>
         */
        public static final String ALERT = "alert";

        static final String[] ALARM_QUERY_COLUMNS = {
            _ID, HOUR, MINUTES, DAYS_OF_WEEK, ALARM_TIME,
            ENABLED, VIBRATE, MESSAGE, ALERT};

        /**
         * These save calls to cursor.getColumnIndexOrThrow()
         * THEY MUST BE KEPT IN SYNC WITH ABOVE QUERY COLUMNS
         */
        public static final int ALARM_ID_INDEX = 0;
        public static final int ALARM_HOUR_INDEX = 1;
        public static final int ALARM_MINUTES_INDEX = 2;
        public static final int ALARM_DAYS_OF_WEEK_INDEX = 3;
        public static final int ALARM_TIME_INDEX = 4;
        public static final int ALARM_ENABLED_INDEX = 5;
        public static final int ALARM_VIBRATE_INDEX = 6;
        public static final int ALARM_MESSAGE_INDEX = 7;
        public static final int ALARM_ALERT_INDEX = 8;
    }
	
	
	
	 /**
     * Sets alert in AlarmManger and StatusBar.  This is what will
     * actually launch the alert when the alarm triggers.
     *
     * Note: In general, apps should call setNextAlert() instead of
     * this method.  setAlert() is only used outside this class when
     * the alert is not to be driven by the state of the db.  "Snooze"
     * uses this API, as we do not want to alter the alarm in the db
     * with each snooze.
     *
     * @param id Alarm ID.
     * @param atTimeInMillis milliseconds since epoch
     */
    static void enableAlert(Context context, int id, String label,
           long atTimeInMillis) {
    }
    
    /**
     * Creates a new Alarm.
     */
    public synchronized static Uri addAlarm(ContentResolver contentResolver) {
        ContentValues values = new ContentValues();
        values.put(AlarmConst.AlarmColumns.HOUR, 8);
        return contentResolver.insert(AlarmColumns.CONTENT_URI, values);
    }
    
    /**
     * Calls the AlarmSettings.reportAlarm interface on alarm with given
     * alarmId
     */
    public synchronized static void getAlarm(
            ContentResolver contentResolver, AlarmSettings alarmSetting,
            int alarmId) {
        Cursor cursor = contentResolver.query(
                ContentUris.withAppendedId(AlarmColumns.CONTENT_URI, alarmId),
                AlarmColumns.ALARM_QUERY_COLUMNS,
                null, null, AlarmColumns.DEFAULT_SORT_ORDER);

        getAlarms(alarmSetting, cursor);
        cursor.close();
    }
    
    private synchronized static void getAlarms(
            AlarmSettings alarmSettings, Cursor cur) {
        if (cur.moveToFirst()) {
            do {
                // Get the field values
                int id = cur.getInt(AlarmColumns.ALARM_ID_INDEX);
                int hour = cur.getInt(AlarmColumns.ALARM_HOUR_INDEX);
                int minutes = cur.getInt(AlarmColumns.ALARM_MINUTES_INDEX);
                int daysOfWeek = cur.getInt(AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX);
                boolean enabled = cur.getInt(AlarmColumns.ALARM_ENABLED_INDEX) == 1 ? true : false;
                boolean vibrate = cur.getInt(AlarmColumns.ALARM_VIBRATE_INDEX) == 1 ? true : false;
                String message = cur.getString(AlarmColumns.ALARM_MESSAGE_INDEX);
                String alert = cur.getString(AlarmColumns.ALARM_ALERT_INDEX);
                alarmSettings.reportAlarm(
                        id, enabled, hour, minutes, new DaysOfWeek(daysOfWeek),
                        vibrate, message, alert);
            } while (cur.moveToNext());
        }
    }

    
    /**
     * Calls the AlarmSettings.reportAlarm interface on alarm with given
     * alarmId
     */
    public synchronized static void getRingtones(
            Context context, AlarmSettings alarmSetting) {
    	ContentResolver cr = context.getContentResolver();
    	// TODO: Should check whether the external sd card is mounted or not  
    	// TODO: Should check null pointer for cursor 
    	/**
    	 * get the audio file in /sdcard/ringtones
    	 */
    	String where = new String();
    	where = "_data LIKE '" + "/" + "sdcard" + "/ringtones/" + "%'";;
    	final Cursor extCursor = cr.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null, where, null, null);
    	if(null != extCursor && extCursor.moveToFirst()){
    		
    	}
    	int count1 = extCursor.getCount();
    	
    	final Cursor internalCursor = cr.query(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, null, null, null, null);
    	if(null != internalCursor && internalCursor.moveToFirst()){
    		
    	}
    	int count2= internalCursor.getCount();
    	
    	final Cursor cur = new MergeCursor(new Cursor[] {internalCursor, extCursor});
    	alarmSetting.reportRingtones(cur);
    }
    
    public synchronized static void getRingtoneUri(Cursor cursor,IAlarmRingtone iAlarmRingtone,int position){
    	Uri uri = null;
    	if(mRingtoneList != null){
    		uri = mRingtoneList.get(position);
    	}
    	iAlarmRingtone.reportRingtoneUri(uri);
    }
    
    public synchronized static void getRingtoneIndex(Cursor cursor,IAlarmRingtone iAlarmRingtone,Uri ringtone){
    	int index = 0;
    	
    	if(mRingtoneIndexList != null){
    		Integer toneIndex = mRingtoneIndexList.get(ringtone);
    		if(null != toneIndex){
    			index = toneIndex;
    		}
    	}
    	iAlarmRingtone.reportRingtoneIndex(index);
    }
    
    public synchronized static void buildRingtoneList(Cursor cursor) {
		int index = 0;
		Uri uri = null;
		
		mRingtoneList.clear();
		mRingtoneIndexList.clear();
		if (cursor != null && cursor.getCount() > 0) {
			if (cursor.moveToFirst()) {
				do{
					int dataIndex = cursor
							.getColumnIndex(MediaStore.Audio.Media.DATA);
					int idIndex = cursor
							.getColumnIndex(MediaStore.Audio.Media._ID);
					String data = cursor.getString(dataIndex);
					if (null != data) {
						String id = cursor.getString(idIndex);
						Uri baseUri = null;
						/**
						 * judge located in /sdcard or internal
						 */
						baseUri = MediaStore.Audio.Media
								.getContentUriForPath(data);
						/**
						 * get media uri
						 */
						uri = Uri.withAppendedPath(baseUri, id);
						
						/**
						 * store results
						 */
						mRingtoneList.put(index, uri);
						mRingtoneIndexList.put(uri, index);
						index ++;
					}
					
				}while(cursor.moveToNext());
			}
		}
	}
    
}